﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Markup;
using System.Globalization;
using System.Text.RegularExpressions;

namespace BrainTechLLC.Silverlight.InputControls
{
    public partial class FilteredTextBox : TextBox
    {
        private string _passwordString = string.Empty;
        public static SolidColorBrush HighlightFocusBrush = new SolidColorBrush(Color.FromArgb(255, 220, 220, 240));

        public object ParentControl { get; set; }

        public static readonly Key[] AlwaysAllowedDuringMask = new Key[] { Key.Down, Key.Up, Key.Left, Key.Right, Key.Home, Key.End, Key.Tab, Key.F4, Key.Back, Key.Delete, Key.Enter, Key.Shift, Key.Ctrl, Key.CapsLock, Key.Alt };
        public static readonly Key[] AlwaysAllowed = new Key[] { Key.Tab, Key.Shift, Key.Enter, Key.Right, Key.Left, Key.Delete, Key.Back };
        public static readonly Key[] NumericKey = new Key[] { Key.D0, Key.D1, Key.D2, Key.D3, Key.D4, Key.D5, Key.D6, Key.D7, Key.D8, Key.D9, Key.NumPad0, Key.NumPad1, Key.NumPad2, Key.NumPad3, Key.NumPad4, Key.NumPad5, Key.NumPad6, Key.NumPad7, Key.NumPad8, Key.NumPad9 };

        public static readonly DependencyProperty ValidatingTypeProperty = DependencyProperty.Register(
            "ValidatingType",
            typeof(Type),
            typeof(FilteredTextBox),
            new PropertyMetadata(null, new PropertyChangedCallback(ValidatingTypeChanged)));

        public static readonly DependencyProperty DataTypeProperty = DependencyProperty.Register(
            "DataType",
            typeof(DataType),
            typeof(FilteredTextBox),
            new PropertyMetadata(DataType.STRING, new PropertyChangedCallback(DataTypeChanged)));

        public static readonly DependencyProperty FormatProperty = DependencyProperty.Register(
            "Format",
            typeof(TextFormat),
            typeof(FilteredTextBox),
            new PropertyMetadata(TextFormat.NONE, new PropertyChangedCallback(FormatChanged)));

        public static readonly DependencyProperty MaskProperty = DependencyProperty.Register(
            "Mask",
            typeof(string),
            typeof(FilteredTextBox),
            new PropertyMetadata(null, new PropertyChangedCallback(MaskChanged)));

        public static readonly DependencyProperty BackgroundForInputControlFocusProperty =
            DependencyProperty.Register(
            "BackgroundForInputControlFocus", typeof(Brush),
            typeof(FilteredTextBox),
            new PropertyMetadata(HighlightFocusBrush, new PropertyChangedCallback(BackgroundForInputControlFocusChanged)));

        public static readonly DependencyProperty PasswordCharProperty = DependencyProperty.Register(
            "PasswordChar", typeof(char), typeof(FilteredTextBox),
            new PropertyMetadata((char)0, new PropertyChangedCallback(PasswordCharChanged)));

        public static readonly DependencyProperty TextWrappingModeProperty = DependencyProperty.Register(
            "TextWrappingMode", typeof(TextWrapping), typeof(FilteredTextBox),
            new PropertyMetadata(TextWrapping.NoWrap, new PropertyChangedCallback(TextWrappingModeChanged)));

        public TextWrapping TextWrappingMode
        {
            get { return (TextWrapping)GetValue(TextWrappingModeProperty); }
            set { SetValue(TextWrappingModeProperty, value); }
        }

        private static void TextWrappingModeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            FilteredTextBox t = (FilteredTextBox)d;
            t.TextWrappingMode = (TextWrapping)e.NewValue;
            t.UpdateTextWrappingMode();
        }

        public void UpdateTextWrappingMode()
        {
            TextWrapping = TextWrappingMode;
        }

        public char PasswordChar
        {
            get { return (char)GetValue(PasswordCharProperty); }
            set { SetValue(PasswordCharProperty, value); }
        }

        private static void PasswordCharChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            FilteredTextBox t = (FilteredTextBox)d;
            t.PasswordChar = (char)e.NewValue;
            t.UpdatePasswordChar((char)e.OldValue);
        }

        public void UpdatePasswordChar(char oldVal)
        {
            if (!string.IsNullOrEmpty(Text) && PasswordChar != 0 && oldVal != PasswordChar)
            {
                int len = Text.Length;
                _passwordString = Text;
                Text = string.Empty.PadLeft(len, PasswordChar);
            }
        }

        public Brush BackgroundForInputControlFocus
        {
            get { return (Brush)GetValue(BackgroundForInputControlFocusProperty); }
            set { SetValue(BackgroundForInputControlFocusProperty, value); }
        }

        private static void BackgroundForInputControlFocusChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            FilteredTextBox t = (FilteredTextBox)d;
            t.BackgroundForInputControlFocus = e.NewValue as Brush;
            t.UpdateBackgroundForInputControlFocusBindings();
        }

        public void UpdateBackgroundForInputControlFocusBindings()
        {
        }

        public string ReadPassword()
        {
            return _passwordString;
        }

        public Type ValidatingType
        {
            get { return (Type)GetValue(ValidatingTypeProperty); }
            set { SetValue(ValidatingTypeProperty, value); }
        }

        public DataType DataType
        {
            get { return (DataType)GetValue(DataTypeProperty); }
            set { SetValue(DataTypeProperty, value); }
        }

        public TextFormat Format
        {
            get { return (TextFormat)GetValue(FormatProperty); }
            set { SetValue(FormatProperty, value); }
        }

        public string Mask
        {
            get { return (string)GetValue(MaskProperty); }
            set { SetValue(MaskProperty, value); }
        }

        private static void ValidatingTypeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            FilteredTextBox t = (FilteredTextBox)d;
            t.ValidatingType = e.NewValue as Type;
        }

        private static void MaxLengthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            FilteredTextBox t = (FilteredTextBox)d;
            t.MaxLength = (int)e.NewValue;
        }

        private static void DataTypeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            FilteredTextBox t = (FilteredTextBox)d;
            t.DataType = (DataType)e.NewValue;
        }

        private static void FormatChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            FilteredTextBox t = (FilteredTextBox)d;
            t.Format = (TextFormat)e.NewValue;
        }

        private static void MaskChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            FilteredTextBox t = (FilteredTextBox)d;
            t.Mask = (string)e.NewValue;
        }

        protected bool ShiftKeyDown { get; set; }
        private Brush OldBackground;

        public FilteredTextBox()
            : base()
        {
            DefaultStyleKey = typeof(FilteredTextBox);            // should be something else?            
            GotFocus += new RoutedEventHandler(FilteredTextBox_GotFocus);
            LostFocus += new RoutedEventHandler(FilteredTextBox_LostFocus);
        }

        void FilteredTextBox_LostFocus(object sender, RoutedEventArgs e)
        {
            FocusHandler.InTextField = false;
        }

        void FilteredTextBox_GotFocus(object sender, RoutedEventArgs e)
        {
            FocusHandler.InTextField = true;
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            this.KeyDown += new KeyEventHandler(InputTextBox_KeyDown);
            this.KeyUp += new KeyEventHandler(InputTextBox_KeyUp);
            this.LostFocus += new RoutedEventHandler(InputTextBox_LostFocus);
            this.GotFocus += new RoutedEventHandler(InputTextBox_GotFocus);

            UpdatePasswordChar(PasswordChar);
        }

        void FilteredTextBox_Loaded(object sender, RoutedEventArgs e)
        {
        }

        public bool MaskFull
        {
            get
            {
                if (string.IsNullOrEmpty(Mask))
                    return true;

                string input = this.Text;

                for (int n = 0; n < input.Length; n++)
                {
                    if (!IsCompatibleWithMask(input[n], n))
                        return false;
                }

                return true;
            }
        }

        public bool IsCompatibleWithMask(char c, int location)
        {
            if (Mask == null)
                return true;

            if (location >= Mask.Length)
                return false;

            if (Mask[location] == '0' && (c < '0' || c > '9'))
                return false;

            if (Mask[location] == '9' && c != ' ' && (c < '0' || c > '9'))
                return false;

            return true;
        }

        public virtual bool IsKeystrokeAllowed(Key key)
        {
            return DataType.IsKeystrokeAllowed(key, Key.None, ShiftKeyDown);
        }

        private void InputTextBox_KeyDown(object sender, KeyEventArgs e)
        {
            string s = e.Key.ToString();
            int selectionStart = SelectionStart;

            if (s.Length == 1 && !IsCompatibleWithMask(s[0], selectionStart))
            {
                e.Handled = true;
                return;
            }

            if (!IsKeystrokeAllowed(e.Key))
            {
                e.Handled = true;
                return;
            }

            if (e.Key == Key.Shift)
            {
                ShiftKeyDown = true;
            }

            if (e.Key.IsIn(AlwaysAllowed))
                return;

            if (PasswordChar != 0)
            {
                char newChar = (char)0;

                if (s.Length == 1)
                {
                    newChar = (ShiftKeyDown) ? s[0] : char.ToLower(s[0]);
                }
                else
                {
                    switch (s.ToUpper())
                    {
                        case "SPACE": newChar = ' '; break;
                        case "SUBTRACT": newChar = '-'; break;
                        case "ADD": newChar = '+'; break;
                        case "DECIMAL": newChar = '.'; break;
                        case "DIVIDE": newChar = '/'; break;
                        case "UNKNOWN": newChar = ','; break; // confirm
                    }

                    if (newChar == 0 && ShiftKeyDown)
                    {
                        switch (s)
                        {
                            case "D1": newChar = '!'; break;
                            case "D2": newChar = '@'; break;
                            case "D3": newChar = '#'; break;
                            case "D4": newChar = '$'; break;
                            case "D5": newChar = '%'; break;
                            case "D6": newChar = '^'; break;
                            case "D7": newChar = '&'; break;
                            case "D8": newChar = '*'; break;
                            case "D9": newChar = '('; break;
                            case "D0": newChar = ')'; break;
                        }
                    }
                    else if (newChar == 0)
                    {
                        switch (s)
                        {
                            case "D1": newChar = '1'; break;
                            case "D2": newChar = '2'; break;
                            case "D3": newChar = '3'; break;
                            case "D4": newChar = '4'; break;
                            case "D5": newChar = '5'; break;
                            case "D6": newChar = '6'; break;
                            case "D7": newChar = '7'; break;
                            case "D8": newChar = '8'; break;
                            case "D9": newChar = '9'; break;
                            case "D0": newChar = '0'; break;
                        }
                    }
                }
                if (newChar != 0)
                {
                    if (selectionStart == _passwordString.Length)
                    {
                        _passwordString = _passwordString + newChar;
                        Text = Text + PasswordChar;
                        SelectionLength = 0;
                        SelectionStart = selectionStart + 1;
                    }
                    else
                    {
                        int selLength = SelectionLength;
                        _passwordString = _passwordString.Substring(0, selectionStart) + newChar + _passwordString.Substring(selectionStart + selLength);
                        Text = Text.Substring(0, selectionStart) + PasswordChar + Text.Substring(selectionStart + selLength);
                        SelectionLength = 0;
                        SelectionStart = selectionStart + 1;
                    }

                    e.Handled = true;
                    return;
                }
            }

            if (this.SelectedText == this.Text)
                return;

            // enforce length limit
            if (MaxLength > 0 && this.Text.Length + 1 > MaxLength)
            {
                e.Handled = true;
                return;
            }

            if (!string.IsNullOrEmpty(Mask) && !e.Key.IsIn(Key.Tab, Key.Back, Key.Left, Key.Right, Key.End, Key.Home, Key.PageDown, Key.PageUp, Key.Shift))
            {
                int location = selectionStart;
                if (location >= Mask.Length)
                {
                    e.Handled = true;
                    return;
                }
                if (Mask[location] == '0' || Mask[location] == '9')
                {
                    if (!e.Key.IsIn(AlwaysAllowedDuringMask))
                    {
                        if (ShiftKeyDown || !e.Key.IsIn(NumericKey))
                        {
                            e.Handled = true;
                            return;
                        }
                    }
                }
            }
        }

        private void InputTextBox_KeyUp(object sender, KeyEventArgs e)
        {
            if (!e.Key.IsIn(Key.Tab, Key.Back, Key.Left, Key.Right, Key.End, Key.Home, Key.PageDown, Key.PageUp, Key.Shift))
            {
                try
                {
                    int selStart = this.SelectionStart;
                    int preLength = this.Text.Length;

                    this.Text = this.Text.GetUnformattedText(DataType, Format);
                    int postLength = this.Text.Length;

                    this.SelectionStart = selStart + (postLength - preLength);
                }
                catch { }
            }

            if (e.Key == Key.Shift)
                ShiftKeyDown = false;
        }

        public string GetStringOnLostFocus(string text, DataType dataType, TextFormat format)
        {
            switch (dataType)
            {
                case DataType.CURRENCY:
                    return "$" + text.GetUnformattedCurrency();

                case DataType.PERCENTAGE:
                    return text.Replace("%", "").GetUnformattedDouble() + "%";

                default:
                    switch (format)
                    {
                        case TextFormat.PHONE:
                            return text.CleanPhoneNumber().FormatPhoneNumber();
                    }
                    break;
            }
            return text.GetUnformattedText(dataType, format);
        }

        private void InputTextBox_LostFocus(object sender, RoutedEventArgs e)
        {
            this.Background = OldBackground;
            string value = this.Text;
            this.Text = GetStringOnLostFocus(value, DataType, Format);
        }

        private string GetStringOnFocus(string text, DataType dataType, TextFormat format)
        {
            DateTime dt;

            switch (DataType)
            {
                case DataType.DATE:
                    if (DateTime.TryParse(text, out dt))
                        return dt.ToString("MM/dd/yyyy");
                    break;

                case DataType.CURRENCY:
                    return text.GetUnformattedCurrency();

                case DataType.INTEGER:
                    return text.GetUnformattedInteger(Format);

                case DataType.DOUBLE:
                    return text.GetUnformattedDouble();

                default:
                    switch (Format)
                    {
                        case TextFormat.PHONE:
                            return text.CleanPhoneNumber();
                    }
                    break;
            }

            return text;
        }

        private void InputTextBox_GotFocus(object sender, RoutedEventArgs e)
        {
            string text = this.Text;
            OldBackground = this.Background;
            this.Background = BackgroundForInputControlFocus;
            this.Text = GetStringOnFocus(text, DataType, Format);
        }
    }


}


